// Movie.cpp: implementation of the Movie class.
//
//////////////////////////////////////////////////////////////////////

#include "Movie.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

Movie::Movie(char filename[],float x_correction, float y_correction)
{
FILE* f;
float n,e,h,p,r,az;
char c;

this->ck = new ulClock;
this->playing = false;

lastFrameReached = false;

this->x_correction = x_correction;
this->y_correction = y_correction;

// create a new ulLinkedList
this->movieList = new ulLinkedList();

// read coords from file
if( (f = fopen(filename,"r")) != NULL)
	{
	while( fscanf(f,"%f%f%f%f%f%f%c",&e,&n,&az,&h,&p,&r,&c) != EOF)
		{
		Frame *fr = (Frame*)malloc(sizeof(Frame));
		fr->e = e;
		fr->n = n;
		fr->a = 0.0f;
		fr->z = -9999.0f;
		fr->az = az;
		fr->h = h;
		fr->p = p;
		fr->r = r;
		fr->lineTransform = new ssgTransform();

		//printf("Frame at %.02f %.02f %.02f %.02f %.02f %.02f\n",fr->e,fr->n,fr->z,fr->h,fr->p,fr->r);

		this->movieList->appendNode(fr);
		}
	fclose(f);
	}
else
	{
	printf("Cannot open movie file %s for reading.\n",filename);
	}
distance = 0.0f;
}

Movie::~Movie()
{

}

void Movie::create(ssgRoot* root)
{
// load main points 3D model
cameraSelectedModel = ssgLoadAC(CAMERA_SELECTED_MODEL_NAME);
cameraSelectedModel -> setName("SELECTED_CAMERA\0");
cameraNormalModel = ssgLoadAC(CAMERA_NORMAL_MODEL_NAME);
cameraNormalModel -> setName("NORMAL_CAMERA\0");

// create a ssgTransfrom node
filmTrack = new ssgTransform();
filmTrack -> setName("MOVIE_TRACK\0");

// create a transform for each waypoint!!!
int n = this->movieList->getNumNodes();

for(int i = 0; i < n; i++)
	{
	Frame *fr = (Frame*)movieList->getNodeData(i);
	fr->z = getHeigth(fr->e + x_correction,fr->n+ y_correction,root);
	fr->a = fr->az - fr->z;

	/* Next frame */
	Frame *fr2 = NULL;

	if(i < (n-1)) 
		{
		fr2 = (Frame*)movieList->getNodeData(i+1);
		fr2->z = getHeigth(fr2->e + x_correction,fr2->n+ y_correction,root);
		fr2->a = fr2->az - fr2->z;

		// calculate distance between two main waypoints
		sgVec3 p1;
		sgVec3 p2;
		
		sgSetVec3(p1,fr->e,fr->n,fr->z);
		sgSetVec3(p2,fr2->e,fr2->n,fr2->z);
		
		createLine(fr2->lineTransform,fr,fr2);
		filmTrack->addKid(fr2->lineTransform);
		//fr2->lineTransform = lt;
		}

	/* WAYPOINTS CREATION */
	ssgTransform* wt = new ssgTransform;
	wt->setName("MOVIE_TRANSFORM\0");
	sgCoord tc;
	sgSetCoord(&tc,fr->e + x_correction,fr->n + y_correction,fr->z + fr-> a,fr->h,fr->p,fr->r);
	if(i == 0)
		{
		fr->cameraTransform = wt;
		//fr->lineTransform = NULL;
		}
	else
		{
		fr->cameraTransform = wt;
		}
	wt->setTransform(&tc);
	// create the selector
	ssgSelector* cameraSelector = new ssgSelector();
	cameraSelector -> setName("CAMERA_SELECTOR\0");
	cameraSelector -> addKid(cameraNormalModel);
	cameraSelector -> addKid(cameraSelectedModel);
	wt->addKid(cameraSelector);
	filmTrack->addKid(wt);
	}
root->addKid(filmTrack);
}

ssgTransform* Movie::createLine(ssgTransform* lt, Frame *c1, Frame *c2)
{
lt = new ssgTransform();
lt->setName("MOVIE_LINE_TRANSFORM\0");

sgCoord coord;
sgSetCoord(&coord,c1->e + x_correction,c1->n + y_correction,c1->z + c1->a,0.0f,0.0f,0.0f);

//printf("Lines at %.02f %.02f %.02f %.02f %.02f %.02f\nLines at %.02f %.02f %.02f %.02f %.02f %.02f\n",c1->e,c1->n,c1->z,c1->h,c1->p,c1->r,c2->e,c2->n,c2->z,c2->h,c2->p,c2->r);

lt->setTransform(&coord);

ssgVertexArray   *vl   = new ssgVertexArray(4);
ssgNormalArray   *nl   = new ssgNormalArray(4);
ssgColourArray   *cl   = new ssgColourArray(4);

// create line points (for GL_QUADS)
sgVec3 pointBL;
sgVec3 pointTL;
sgVec3 pointBR;
sgVec3 pointTR;

sgSetVec3(pointBL,0.0f,0.0f,-MOVIE_LINE_HEIGTH/2);
sgSetVec3(pointTL,0.0f,0.0f,MOVIE_LINE_HEIGTH/2);
sgSetVec3(pointBR,(c2->e + x_correction) - (c1->e + x_correction),(c2->n + y_correction) - (c1->n + y_correction),(c2->z + c2->a) - (c1->z + c1->a) - MOVIE_LINE_HEIGTH/2);
sgSetVec3(pointTR,(c2->e + x_correction) - (c1->e + x_correction),(c2->n + y_correction) - (c1->n + y_correction),(c2->z + c2->a) - (c1->z + c1->a) + MOVIE_LINE_HEIGTH/2);

// make normals
sgVec3 normBL;
sgVec3 normBR;
sgVec3 normTL;
sgVec3 normTR;
sgVec3 norm;

sgMakeNormal(normBL,pointBL,pointBR,pointTL);	
sgMakeNormal(normBR,pointBR,pointTR,pointBL);   
sgMakeNormal(normTL,pointTL,pointBL,pointTR);   
sgMakeNormal(normTR,pointTR,pointTL,pointBR);   
sgMakeNormal(norm,pointBL,pointTL,pointBR);

// choose colors (RED is down, GREEN is up)
// create colors (one per vertex)
sgVec4 colorBL;
sgVec4 colorBR;
sgVec4 colorTL;
sgVec4 colorTR;

sgSetVec4(colorBL,LINE_BLUE);
sgSetVec4(colorTL,LINE_BLUE);
sgSetVec4(colorBR,LINE_BLUE);
sgSetVec4(colorTR,LINE_BLUE);

vl->add(pointBL);
vl->add(pointBR);
vl->add(pointTR);
vl->add(pointTL);

nl->add(normBL);
nl->add(normBR);
nl->add(normTR);
nl->add(normTL);

cl->add(colorBL);
cl->add(colorBR);
cl->add(colorTR);
cl->add(colorTL);

vl->add(pointBL);
vl->add(pointTL);
vl->add(pointTR);
vl->add(pointBR);

nl->add(norm);
nl->add(norm);
nl->add(norm);
nl->add(norm);

cl->add(colorBL);
cl->add(colorTL);
cl->add(colorTR);
cl->add(colorBR);

// create a new vertex table
ssgVtxTable *leaf = new ssgVtxTable(GL_QUADS,vl,nl,NULL,cl);
ssgSimpleState *s = new ssgSimpleState();
s->enable(GL_BLEND);
s->disable(GL_LIGHTING);
leaf->setState(s);

// set the leaf state
leaf -> setName  ("MOVIE_LINE");

c2->lineTransform = lt;

// add the line transform
lt->removeAllKids();
lt->addKid( leaf );

// attach it to the brach
return lt;
}

float Movie::getHeigth(float x, float y, ssgRoot *root)
{
sgMat4 objectPositionInverseMatrix;
sgMakeIdentMat4 ( objectPositionInverseMatrix ) ;

objectPositionInverseMatrix[3][SG_X] =  - (x);
objectPositionInverseMatrix[3][SG_Y] =  - (y);
objectPositionInverseMatrix[3][SG_Z] =  0.0f;

// Test vector for HOT: vector is perpendicular to the xy plane
sgVec3 testVectorHOT;

testVectorHOT[SG_X] = 0.0f;
testVectorHOT[SG_Y] = 0.0f;
testVectorHOT[SG_Z] = 100000.0f;

// ssgHit structure, it will contain the colliding triangles
ssgHit *hitResults;
int     numberOfHits;
float   hot = -9999.0f;

// Test database for HOT...
numberOfHits = ssgHOT ( root, testVectorHOT, objectPositionInverseMatrix, &hitResults );
			
// Read the results
for ( int k = 0 ; k < numberOfHits ; k++ )
	{
	ssgHit *h = &hitResults [ k ] ;
	
	float hgt = - h->plane[3] / h->plane[2] ;
	if ( hgt >= hot )
		{
		bool terrain = false;
		bool go = true;
		for(int p = 0; (p < h->getNumPathEntries()) && go; p++)
			{
			//printf("%s\n",h->getPathEntry(p)->getName());
			if(ulStrEqual(h->getPathEntry(p)->getName(),"LAND_TRANSFORM\0"))
				{
				terrain = true;
				hot = hgt ;
				}
			else if(ulStrEqual(h->getPathEntry(p)->getName(),"MOVIE_TRACK\0"))
				{
				terrain = false;
				go = false;
				}
			}
		}
	}

return hot;
}

void Movie::fprint(FILE* f)
{
int n = movieList->getNumNodes();

for(int i = 0; i < n; i++)
	{
	Frame* fr = (Frame*)movieList->getNodeData(i);
	fprintf(f,"%f %f %f %f %f %f\n",fr->e,fr->n,fr->az,fr->h,fr->p,fr->r);
	}
}

void Movie::add(ssgRoot* root, float x, float y, float z)
{
int num = this->movieList->getNumNodes();

if(num > 0)
	{
	// retrieve last node
	Frame* fr = (Frame*)movieList->getNodeData(num-1);
	Frame* fr2 = (Frame*)malloc(sizeof(Frame));
	fr2->e = x - x_correction;
	fr2->n = y - y_correction;
	fr2->a = MOVIE_MIN_HEIGTH;
	fr2->z = getHeigth(fr2->e + x_correction,fr2->n+ y_correction,root);
	fr2->az = fr2->z + fr2->a;
	fr2->h = 0.0f;
	fr2->p = 0.0f;
	fr2->r = 0.0f;
	fr2->lineTransform = new ssgTransform();
	this->movieList->appendNode(fr2);

	// calculate distance between two main waypoints
	sgVec3 p1;
	sgVec3 p2;

	sgSetVec3(p1,fr->e,fr->n,fr->h);
	sgSetVec3(p2,fr2->e,fr2->n,fr2->h);
		
	createLine(fr2->lineTransform,fr,fr2);

	//filmTrack->addKid(lt);
	/* WAYPOINTS CREATION */
	ssgTransform* wt = new ssgTransform;
	wt->setName("MOVIE_TRANSFORM\0");
	sgCoord tc;
	sgSetCoord(&tc,fr2->e + x_correction,fr2->n + y_correction,fr2->az,fr2->h,fr2->p,fr2->r);
	fr2->cameraTransform = wt;
	//fr2->lineTransform = lt;
	wt->setTransform(&tc);
	// create the selector
	ssgSelector* cameraSelector = new ssgSelector();
	cameraSelector -> setName("CAMERA_SELECTOR\0");
	cameraSelector -> addKid(cameraNormalModel);
	cameraSelector -> addKid(cameraSelectedModel);
	cameraSelector -> selectStep(MOVIE_NORMAL);
	wt->addKid(cameraSelector);
	filmTrack->addKid(wt);
	filmTrack->addKid(fr2->lineTransform);
	}
else
	{
	Frame* fr0 = (Frame*)malloc(sizeof(Frame));
	fr0->e = x - x_correction;
	fr0->n = y - y_correction;
	fr0->a = MOVIE_MIN_HEIGTH;
	fr0->z = getHeigth(fr0->e + x_correction,fr0->n+ y_correction,root);
	fr0->az = fr0->a + fr0->z;
	fr0->h = 0.0f;
	fr0->p = 0.0f;
	fr0->r = 0.0f;
	fr0->lineTransform = new ssgTransform();
	this->movieList->appendNode(fr0);
	/* WAYPOINTS CREATION */
	ssgTransform* wt = new ssgTransform;
	wt->setName("MOVIE_TRANSFORM\0");
	sgCoord tc;
	sgSetCoord(&tc,fr0->e + x_correction,fr0->n + y_correction,fr0->z + fr0->a,0.0f,0.0f,0.0f);
	fr0->cameraTransform = wt;
	//fr0->lineTransform = NULL;
	wt->setTransform(&tc);
	// create the selector
	ssgSelector* cameraSelector = new ssgSelector();
	cameraSelector -> setName("CAMERA_SELECTOR\0");
	cameraSelector -> addKid(cameraNormalModel);
	cameraSelector -> addKid(cameraSelectedModel);
	cameraSelector -> selectStep(MOVIE_NORMAL);
	wt->addKid(cameraSelector);
	filmTrack->addKid(wt);
	}
}

Frame* Movie::get(ssgTransform* t)
{
int n = this->movieList->getNumNodes();
Frame* found = NULL;

for(int i = 0; i < n; i++)
	{
	Frame* fr = (Frame*)movieList->getNodeData(i);
		if(fr->cameraTransform == t)
			found = fr;
	}
return found;
}

Frame* Movie::get(ssgTransform* t,int what)
{
int n = this->movieList->getNumNodes();
Frame* found = NULL;

for(int i = 0; i < n; i++)
	{
	Frame* fr = (Frame*)movieList->getNodeData(i);
	printf("Camera (%d) has got line (%p) and you're looking for (%p)\n",i,fr->lineTransform,t);
		if(what == CAMERA)
			{
			if(fr->cameraTransform == t)
				found = fr;
				return fr;
			}
		else if(what == TRACK)
			{
			if(fr->lineTransform == t)
				{
				found = fr;
				printf("Found at (%d)\n",i);
				return fr;
				}
			}	
	}
return found;
}

int Movie::getPosition(Frame *fr)
{
return movieList->getNodePosition(fr);
}

void Movie::remove(Frame *fr)
{

if(fr != NULL)
	{
	int n = movieList->getNumNodes();
	int selected_pos = movieList->getNodePosition(fr);
	int before_pos = selected_pos - 1;
	int after_pos = selected_pos + 1;

	Frame *selected = fr;
	Frame *before   = NULL;
	Frame *after    = NULL;

	if( (before_pos < 0) ) 
		{
		if( (after_pos >= n) ) // selected waypoint was the first and only
			{
			filmTrack->removeKid(selected->cameraTransform);
			selected->cameraTransform = NULL;
			movieList -> removeNode(selected);
			}
		else // selected waypoint was the first
			{
			after = (Frame*)movieList->getNodeData(after_pos);
			filmTrack->removeKid(after->lineTransform);
			filmTrack->removeKid(selected->cameraTransform);
			selected->cameraTransform = NULL;
			movieList->removeNode(selected);
			}
		}
	else if( (after_pos >= n) ) // selected waypoint was the last
		{
			filmTrack->removeKid(selected->lineTransform);
			filmTrack->removeKid(selected->cameraTransform);
			selected->cameraTransform = NULL;
			movieList->removeNode(selected);
		}
	else // selected waypoint was one of the middle
		{
			before = (Frame*)movieList->getNodeData(before_pos);
			after  = (Frame*)movieList->getNodeData(after_pos);
			filmTrack->removeKid(after->lineTransform);
			filmTrack->removeKid(selected->lineTransform);
			filmTrack->removeKid(selected->cameraTransform);
			selected->cameraTransform = NULL;
			movieList->removeNode(selected);
			filmTrack->addKid(createLine(after->lineTransform,before,after));
		}
	}
}

void Movie::insert(ssgRoot* root, int bp, int ap, float x, float y, float z)
{
Frame* after  = (Frame*)movieList->getNodeData(ap);
Frame* before = (Frame*)movieList->getNodeData(bp);

Frame* selected = (Frame*)malloc(sizeof(Frame));

selected->e = x - x_correction;
selected->n = y - y_correction;
selected->z = getHeigth(selected->e + x_correction,selected->n+ y_correction,root);
selected->az = z;
selected->a = selected->az - selected->z;
selected->h = 0.0f;
selected->p = -10.0f;
selected->r = 0.0f;

selected->lineTransform = new ssgTransform();
this->movieList->insertNode(selected,ap); 

filmTrack->removeKid(after->lineTransform);

filmTrack->addKid(createLine(selected->lineTransform,before,selected));
filmTrack->addKid(createLine(after->lineTransform,selected,after));

/* WAYPOINTS CREATION */
ssgTransform* wt = new ssgTransform;
wt->setName("MOVIE_TRANSFORM\0");
sgCoord tc;
sgSetCoord(&tc,selected->e + x_correction,selected->n + y_correction,selected->az,selected->h,selected->p,selected->r);
selected->cameraTransform = wt;
wt->setTransform(&tc);
// create the selector
ssgSelector* cameraSelector = new ssgSelector();
cameraSelector -> setName("CAMERA_SELECTOR\0");
cameraSelector -> addKid(cameraNormalModel);
cameraSelector -> addKid(cameraSelectedModel);
cameraSelector -> selectStep(MOVIE_NORMAL);
wt->addKid(cameraSelector);
filmTrack->addKid(wt);
}

void Movie::reposition(ssgRoot* root, Frame *fr, float x, float y, float z)
{
printf("Repositioning\n");
if(fr != NULL)
	{
	int n = movieList->getNumNodes();
	int selected_pos = movieList->getNodePosition(fr);
	int before_pos = selected_pos - 1;
	int after_pos = selected_pos + 1;

	Frame *selected = fr;
	Frame *before   = NULL;
	Frame *after    = NULL;

	if( (before_pos < 0) ) 
		{
		if( (after_pos >= n) ) // selected waypoint was the first and only
			{
			printf("First and only\n");
			sgCoord new_pos;
			sgSetCoord(&new_pos,x,y,z+selected->a,selected->h,selected->p,selected->r);
			selected->cameraTransform->setTransform(&new_pos);
			}
		else // selected waypoint was the first
			{
			printf("First\n");
			after = (Frame*)movieList->getNodeData(after_pos);
			filmTrack->removeKid(after->lineTransform);
			sgCoord new_pos;
			sgSetCoord(&new_pos,x,y,z+selected->a,selected->h,selected->p,selected->r);
			selected->cameraTransform->setTransform(&new_pos);
			filmTrack->addKid(createLine(after->lineTransform,selected,after));
			}
		}
	else if( (after_pos >= n) ) // selected waypoint was the last
		{
			printf("Last\n");
			before = (Frame*)movieList->getNodeData(before_pos);
			filmTrack->removeKid(selected->lineTransform);
			sgCoord new_pos;
			sgSetCoord(&new_pos,x,y,z+selected->a,selected->h,selected->p,selected->r);
			selected->cameraTransform->setTransform(&new_pos);
			filmTrack->addKid(createLine(selected->lineTransform,before,selected));
		}
	else // selected waypoint was one of the middle
		{
			before = (Frame*)movieList->getNodeData(before_pos);
			after  = (Frame*)movieList->getNodeData(after_pos);
			printf("Before %d\n",filmTrack->getNumKids());
			printf("Looking for %p\n",selected->lineTransform);
			printf("Looking for %p\n",after->lineTransform);
			filmTrack->removeKid(selected->lineTransform);
			filmTrack->removeKid(after->lineTransform);
			printf("After %d\n",filmTrack->getNumKids());
			printf("Middle\n");
			sgCoord new_pos;
			sgSetCoord(&new_pos,x,y,z+selected->a,selected->h,selected->p,selected->r);
			selected->cameraTransform->setTransform(&new_pos);
			filmTrack->addKid(createLine(selected->lineTransform,before,selected));
			filmTrack->addKid(createLine(after->lineTransform,selected,after));
		}
	}
}

void Movie::start()
{
this->playingFrameNumber = 0;
this->nextFrameNumber    = playingFrameNumber + 1;
this->ck->reset();

distance = 0.0f;

Frame* fr = (Frame*)movieList->getNodeData(this->playingFrameNumber);

if(fr!=NULL) sgSetCoord(&currentCameraPosition, fr->e + x_correction, fr->n + y_correction, fr->z + fr->a, fr->h, fr->p, fr->r);
this->filmTrack->clrTraversalMaskBits(SSGTRAV_CULL);
}

void Movie::end()
{
this->playingFrameNumber = 0;
this->nextFrameNumber    = playingFrameNumber + 1;
this->ck->reset();
this->filmTrack->setTraversalMaskBits(SSGTRAV_CULL);
this->lastFrameReached = false;
this->stop();
}

void Movie::play()
{
if(!playing)
	{
	playing = true;
	ck->reset();
	}
}

void Movie::update()
{
if(playing && !lastFrameReached)
	{
	this->ck->update();

	float dt = (float)ck->getDeltaTime();

	this->LERPframes(dt); 
	}
}

void Movie::stop()
{
playing = false;
}

void Movie::next()
{
}

void Movie::before()
{
}

void Movie::forward()
{
if(this->playingFrameNumber+2 < movieList->getNumNodes())
	{
	this->playingFrameNumber++;
	this->nextFrameNumber = this->playingFrameNumber + 1;
	distance = 0.0f;
	LERPframes(0.0f);
	this->stop();
	}
}

void Movie::rewind()
{
if(this->playingFrameNumber > 0)
	{
	this->playingFrameNumber--;
	this->nextFrameNumber = this->playingFrameNumber + 1;
	distance = 0.0f;
	LERPframes(0.0f);
	this->stop();
	}
}

void Movie::LERPframes(float dt)
{
sgVec3 v1,w1;
sgVec3 v2,w2;
sgVec3 v3,w3;

Frame* f1 = (Frame*)movieList->getNodeData(playingFrameNumber);
Frame* f2 = (Frame*)movieList->getNodeData(nextFrameNumber);

sgSetVec3(v1,f1->e + x_correction, f1->n + y_correction , f1->z + f1->a);
sgSetVec3(v2,f2->e + x_correction, f2->n + y_correction , f2->z + f2->a);

sgSetVec3(w1,f1->h , f1->p , f1->r);
sgSetVec3(w2,f2->h , f2->p , f2->r);
	
float dst = dt * MOVIE_SPEED;

distance += dst;

float distance_tot = sgDistanceVec3(v1,v2);

float delta = distance / distance_tot;

if(delta < 1.0f)
	{
	sgLerpVec3 ( v3, v1, v2, delta );
	sgLerpAnglesVec3 ( w3, w1, w2, delta );
	}
else
	{
	delta = 1.0f;

	sgLerpVec3 ( v3, v1, v2, delta );
	sgLerpAnglesVec3 ( w3, w1, w2, delta );

	this->playingFrameNumber++;
	this->nextFrameNumber++;

	if(this->playingFrameNumber+1 >= movieList->getNumNodes())
		{
		this->lastFrameReached = true;
		}

	distance = 0.0f;
	}
sgSetCoord(&currentCameraPosition, v3[0], v3[1], v3[2], w3[0], w3[1], w3[2]);
}

sgCoord* Movie::getLERPCameraPosition()
{
return &currentCameraPosition;
}